package com.doupi.partner.api.core;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.doupi.partner.util.AssertUtils;
import com.doupi.partner.util.RedisUtils;
import com.doupi.partner.util.RuntimeBusinessException;
import com.doupi.partner.util.SpringContextUtils;
import com.doupi.partner.util.constant.UtilConstants;

/**
 * 业务处理门面的抽象类
 */
@Component
public abstract class AbstractBiz {

	protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

	protected final static String USER_IP_VISIT_CACHE_COUNT_KEY_PREFIX = "IP_VISIT_COUNT";

	protected final static String NO_LOGIN_MESSAGE = "用户未登录";

	protected final static String NO_AUTH_MESSAGE = "无权限";

	protected RedisUtils redisUtils;

	@Value("#{doupi_partner_util_config.ip_limit_count}")
	protected int maxIpLimitCount;

	public AbstractBiz() {
		this.redisUtils = SpringContextUtils.getBeanByName("redisUtils");
	}

	// 登录校验，需要用户显示在子类实现execute方法中调用
	protected void loginCheck(ApiRequest request) {
		// TODO jwt放的不是用户id时候，需要修改实现
		Integer userId = (Integer) request.getJwt();
		AssertUtils.assertNotNull(userId, UtilConstants.NO_LOGIN_STATUS_CODE, NO_LOGIN_MESSAGE);

		String token = request.getToken();
		AssertUtils.assertNotNull(token, UtilConstants.NO_LOGIN_STATUS_CODE, NO_LOGIN_MESSAGE);

		String tokenOrigin = redisUtils.get(UtilConstants.USER_TOKEN_CACHE_KEY_PREFIX + userId);
		AssertUtils.assertEqual(tokenOrigin, token, UtilConstants.NO_LOGIN_STATUS_CODE, NO_LOGIN_MESSAGE);
	}

	// 权限校验，需要具体的子类实现
	protected void authCheck(ApiRequest request) {
		if (null == request) {
			throw RuntimeBusinessException.createException(UtilConstants.NO_AUTH_STATUS_CODE, NO_AUTH_MESSAGE);
		}
	}

	// 安全校验，需要用户显示在子类实现execute方法中调用
	protected void safeCheck(ApiRequest request) {
		String ipLimitKey = USER_IP_VISIT_CACHE_COUNT_KEY_PREFIX + request.getBiz() + request.getIp();
		String ipLimitCountString = redisUtils.get(ipLimitKey);
		int ipLimitCount = 0;
		if (StringUtils.isNotBlank(ipLimitCountString)) {
			ipLimitCount = Integer.parseInt(ipLimitCountString);
		}
		if (ipLimitCount > maxIpLimitCount) {
			throw RuntimeBusinessException.createException(UtilConstants.IP_FORBIDDEN_STATUS_CODE, "您的请求次数太多喽");
		}
		// TODO IP受限时效 1小时几次 受限后每次失效时间递增
		redisUtils.setex(ipLimitKey, String.valueOf(++ipLimitCount), 60 * 60);
	}

	/**
	 * 业务统一抽象执行方法，业务实现类实现该方法并在方法内做具体的业务实现
	 * 
	 * @param request
	 *            请求对象 {@link ApiRequest}
	 * @param response
	 *            响应对象 {@link ApiResponse}
	 */
	protected abstract void execute(ApiRequest request, ApiResponse response);

	/**
	 * 具体执行方法
	 * 
	 * @param request
	 *            请求对象 {@link ApiRequest}
	 * @param response
	 *            响应对象 {@link ApiResponse}
	 */
	public void doExecute(ApiRequest request, ApiResponse response) {
		// 安全检查
		if (this.getClass().isAnnotationPresent(Security.class)) {
			this.safeCheck(request);
		}

		// 登录检查
		if (this.getClass().isAnnotationPresent(Login.class)) {
			this.loginCheck(request);
		}

		// 授权检查
		if (this.getClass().isAnnotationPresent(Authorization.class)) {
			this.authCheck(request);
		}

		this.execute(request, response);
	}
}
